use std::{
    collections::HashMap,
    sync::{
        atomic::{AtomicUsize, Ordering},
        Arc, RwLock,
    },
};

use axum::{extract::State, http::StatusCode, Json};
use serde::{Deserialize, Serialize};

use crate::jwt::{Claims, HttpError};

static ID: AtomicUsize = AtomicUsize::new(0);

#[derive(Serialize, Clone)]
pub struct Todo {
    pub id: usize,
    pub user_id: usize,
    pub title: String,
    pub completed: bool,
}

#[derive(Deserialize)]
pub struct CreateTodo {
    pub title: String,
}

#[derive(Deserialize)]
pub struct UpdateTodo {
    id: usize,
    title: Option<String>,
    completed: Option<bool>,
}

#[derive(Deserialize)]
pub struct DeleteTodo {
    id: usize,
}

pub type Db = Arc<RwLock<HashMap<usize, Todo>>>;

pub async fn todos_handler(
    claims: Claims,
    State(todo_list): State<Db>,
) -> Result<Json<Vec<Todo>>, HttpError> {
    if let Ok(items) = todo_list.read() {
        let user_id = claims.id;
        Ok(Json(
            items
                .iter()
                .filter(|(_, x)| x.user_id == user_id)
                .map(|(_, x)| x.clone())
                .collect(),
        ))
    } else {
        Err(HttpError::Internal)
    }
}

pub async fn todo_create_handler(
    claims: Claims,
    State(todo_list): State<Db>,
    Json(payload): Json<CreateTodo>,
) -> Result<StatusCode, HttpError> {
    if let Ok(mut list) = todo_list.write() {
        let next_id = get_increase_id();
        let todo = Todo {
            id: next_id,
            user_id: claims.id,
            title: payload.title,
            completed: false,
        };
        list.insert(todo.id, todo);
        Ok(StatusCode::CREATED)
    } else {
        Err(HttpError::Internal)
    }
}

pub async fn todo_update_handler(
    claims: Claims,
    State(db): State<Db>,
    Json(update): Json<UpdateTodo>,
) -> Result<StatusCode, HttpError> {
    let user_id = claims.id;
    if let Ok(mut items) = db.write() {
        if let Some(item) = items.get_mut(&update.id) {
            if item.user_id == user_id {
                if let Some(title) = update.title {
                    item.title = title;
                }
                if let Some(completed) = update.completed {
                    item.completed = completed;
                }
                return Ok(StatusCode::OK);
            }
        }
    }
    Err(HttpError::Internal)
}

pub async fn todo_delete_handler(
    claims: Claims,
    State(db): State<Db>,
    Json(delete): Json<DeleteTodo>,
) -> Result<StatusCode, HttpError> {
    let user_id = claims.id;
    if let Ok(mut items) = db.write() {
        if let Some(item) = items.get(&delete.id) {
            if item.user_id == user_id {
                items.remove(&delete.id);
                return Ok(StatusCode::OK);
            }
        }
    }
    return Err(HttpError::Internal);
}

fn get_increase_id() -> usize {
    ID.fetch_add(1, Ordering::Relaxed)
}
